package validation;

import org.netbeans.validation.api.*;
import org.netbeans.validation.api.builtin.stringvalidation.StringValidators;
import org.netbeans.validation.api.ui.ValidationUI;
import org.netbeans.validation.api.ui.swing.AbstractValidationListener;
import org.netbeans.validation.api.ui.swing.SwingComponentDecorationFactory;
import org.netbeans.validation.api.ui.swing.ValidationPanel;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.io.File;

public class ValidationDemo {
    public static void main(String[] args) throws Exception {
        // Set the system look and feel
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        final JFrame jf = new JFrame("Validators Demo");
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Here we create our ValidationPanel
        // It has a built-in ValidationGroup we can use
        // we will just call pnl.getValidationGroup() and add validators to it tied to components
        final ValidationPanel pnl = new ValidationPanel();
        jf.setContentPane(pnl);

        // A panel to hold most of our components that we will be validating
        JPanel inner = new JPanel();
        inner.setLayout(new BoxLayout(inner, BoxLayout.Y_AXIS));
        pnl.setInnerComponent(inner);
        JLabel lbl;
        JTextField field;

        // Here's our first thing to validate
        lbl = new JLabel("Not a Java keyword:");
        inner.add(lbl);
        field = new JTextField("listener");
        field.setName("Non Identifier");
        inner.add(field);

        // so, we will get a validator, which does trim strings
        // (that's the boolean argument set to true),
        // which will not accept empty strings or java keywords
        Validator<String> d = StringValidators.trimString(ValidatorUtils.merge(
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.REQUIRE_JAVA_IDENTIFIER));

        // now we add it to the validation group
        pnl.getValidationGroup().add(field, d);

        // This one is similar to the example above, but it will split the string
        // into component parts divided by '.' characters first
        lbl = new JLabel("Legal java package name:");
        inner.add(lbl);
        field = new JTextField("com.foo.bar.baz");
        field.setName("package name");
        inner.add(field);
        Validator<String> ddd = ValidatorUtils.merge(StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.JAVA_PACKAGE_NAME, StringValidators.MAY_NOT_END_WITH_PERIOD);
        pnl.getValidationGroup().add(field, ddd);

        lbl = new JLabel("IP Address or Host Name:");
        inner.add(lbl);
        field = new JTextField("127.0.0.1");
        field.setName("Address");
        inner.add(field);
        Validator<String> dd = ValidatorUtils.merge(StringValidators.HOST_NAME_OR_IP_ADDRESS);
        pnl.getValidationGroup().add(field, dd);

        lbl = new JLabel("Must be a non-negative integer");
        inner.add(lbl);
        field = new JTextField("42");
        field.setName("the number");
        inner.add(field);

        // Note that we're very picky here
        // require non-negative number and require valid number don't care that we want an interger
        // we also need to require valid integer
        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.REQUIRE_VALID_NUMBER,
                StringValidators.REQUIRE_VALID_INTEGER,
                StringValidators.REQUIRE_NON_NEGATIVE_NUMBER);

        lbl = new JLabel("Email address");
        inner.add(lbl);
        field = new JTextField("Foo Bar <foo@bar.com>");
        field.setName("Email address");
        inner.add(field);

        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.EMAIL_ADDRESS);

        lbl = new JLabel("Hexadecimal Number");
        inner.add(lbl);
        field = new JTextField("CAFEBEBE");
        field.setName("hex number");
        inner.add(field);
        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.VALID_HEXADECIMAL_NUMBER);

        lbl = new JLabel("No spaces: ");
        inner.add(lbl);
        field = new JTextField("ThisTextHasNoSpaces");
        field.setName("No spaces");
        inner.add(field);
        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.NO_WHITESPACE);

        lbl = new JLabel("Enter a URL");
        inner.add(lbl);
        field = new JTextField("https://netbeans.org/");
        field.setName("URL");
        inner.add(field);
        pnl.getValidationGroup().add(field, StringValidators.URL_MUST_BE_VALID);

        lbl = new JLabel("file that exists");
        inner.add(lbl);
        // Find a random file so we can populate the field with a valid initial value, if possible
        File userdir = new File(System.getProperty("user.dir"));
        File aFile = null;
        for (File f : userdir.listFiles()) {
            if (f.isFile()) {
                aFile = f;
                break;
            }
        }
        field = new JTextField(aFile == null ? "" : aFile.getAbsolutePath());
        // we can call field.setName("File")
        // Note there is an alternative to field.setName()
        // if we are using that for some other purpose
        //
        field.setName("File");
        inner.add(field);
        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.FILE_MUST_BE_FILE);

        lbl = new JLabel("Folder that exists");
        inner.add(lbl);
        field = new JTextField(System.getProperty("user.dir"));
        field.setName("folder");
        inner.add(field);
        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.FILE_MUST_BE_DIRECTORY);

        lbl = new JLabel("Valid file name");
        inner.add(lbl);
        field = new JTextField("Validators.java");
        field.setName("File Name");
        inner.add(field);
        pnl.getValidationGroup().add(field,
                StringValidators.REQUIRE_NON_EMPTY_STRING,
                StringValidators.REQUIRE_VALID_FILENAME);

        // here we will do custom validation of a JColorChooser
        final JColorChooser chooser = new JColorChooser();
        // Use an intermediary panel to keep the layout from jumping when the problem is show/hidden
        final JPanel ccPanel = new JPanel();
        ccPanel.add(chooser);
        // Add it to the main panel because GridLayout will make it to small
        // ValidationPanel uses BorderLayout (and will throw an exception if you try to change it)
        pnl.add(ccPanel, BorderLayout.EAST);

        // Set a default value that won't show an error
        chooser.setColor(new Color(191, 86, 86));

        // ColorValidator is defined below
        final ColorValidator colorValidator = new ColorValidator();
        final ValidationUI ccDecorator = SwingComponentDecorationFactory.getDefault().decorationFor(chooser);

        // Note if we could also implement Validator directly on this class
        // however it's more reusable if we don't
        class ColorListener extends AbstractValidationListener<JColorChooser, Color> implements ChangeListener {
            ColorListener() {
                super(JColorChooser.class, chooser, ccDecorator, colorValidator);
            }

            @Override
            public void stateChanged(ChangeEvent e) {
                super.performValidation();
            }

            @Override
            protected Color getModelObject(JColorChooser jColorChooser) {
                return jColorChooser.getColor();
            }
        }
        ColorListener cl = new ColorListener();
        chooser.getSelectionModel().addChangeListener(cl);
        // Add our custom validation code to the validation group
        // XXX even when problems.add is called below, nothing appears -- why
        pnl.getValidationGroup().addItem(cl, false);
        boolean onClicked = pnl.showOkCancelDialog("Validation Demo");
        System.out.println(onClicked ? "User clicked OK" : "User did not click OK");
        System.exit(0);
    }

    private static final class ColorValidator extends AbstractValidator<Color> {
        ColorValidator() {
            super(Color.class);
        }

        @Override
        public void validate(Problems problems, String compName, Color model) {
            // Convert the color to Hue/Saturation/Brightness
            // scaled from 0F to 1.0F
            float[] hsb = Color.RGBtoHSB(model.getRed(), model.getGreen(), model.getBlue(), null);
            if (hsb[2] < 0.25) {
                // Dark colors cause a fatal error
                problems.add("Color is to dark");
            } else if (hsb[2] > 0.9) {
                problems.add("Color is very bright", Severity.INFO);
            }
            if (hsb[1] > 0.8) {
                problems.add("Color is very saturated", Severity.WARNING);
            }
        }
    }
}
